package cn.lijiajia3515.cairo.captcha.image.graph;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.RandomUtil;
import cn.lijiajia3515.cairo.captcha.image.ImageCaptcha;
import cn.lijiajia3515.cairo.captcha.utils.GifEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;

import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * gif 画验证码图 处理器
 */
@Slf4j
public class GifGraphImageCaptchaHandler extends AbstractGraphImageCaptchaHandler {

	public GifGraphImageCaptchaHandler() {
		super();
	}

	public GifGraphImageCaptchaHandler(int[][] color, String[] fontNames) {
		super(color, fontNames);
	}

	@Override
	public ImageCaptcha.ImageCaptchaDetail getDetail(GraphImageArgs args) {
		int width = args.getWidth();
		int height = args.getHeight();
		String code = args.getImageStr();
		// 随机生成每个文字的颜色
		Color[] fontColor = new Color[code.length()];
		for (int i = 0; i < code.length(); i++) {
			fontColor[i] = color();
		}
		// 随机生成贝塞尔曲线参数
		int x1 = 5, y1 = RandomUtil.randomInt(5, height / 2);
		int x2 = width - 5, y2 = RandomUtil.randomInt(height / 2, height - 5);
		int ctrlx = RandomUtil.randomInt(width / 4, width / 4 * 3), ctrly = RandomUtil.randomInt(5, height - 5);
		if (RandomUtil.randomInt(2) == 0) {
			int ty = y1;
			y1 = y2;
			y2 = ty;
		}
		int ctrlx1 = RandomUtil.randomInt(width / 4, width / 4 * 3), ctrly1 = RandomUtil.randomInt(5, height - 5);
		int[][] besselXY = new int[][]{{x1, y1}, {ctrlx, ctrly}, {ctrlx1, ctrly1}, {x2, y2}};

		try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
			// 开始画gif每一帧
			GifEncoder gifEncoder = new GifEncoder();
			gifEncoder.setQuality(180);
			gifEncoder.setDelay(250);
			gifEncoder.setRepeat(0);
			gifEncoder.start(out);
			for (int i = 0; i < code.length(); i++) {
				BufferedImage frame = graphicsImage(fontColor, code, i, besselXY, width, height);
				gifEncoder.addFrame(frame);
				frame.flush();
			}
			gifEncoder.finish();
			out.flush();
			return ImageCaptcha.ImageCaptchaDetail.builder().bytes(out.toByteArray()).mediaType(MediaType.IMAGE_GIF).build();
		} catch (IOException e) {
			throw new IORuntimeException("无法生成gif图片", e);
		}
	}

	/**
	 * 画随机码图
	 *
	 * @param fontColor 随机字体颜色
	 * @param code      编码
	 * @param flag      透明度
	 * @param besselXY  干扰线参数
	 * @param width     宽度
	 * @param height    高度
	 * @return BufferedImage
	 */
	protected BufferedImage graphicsImage(Color[] fontColor, String code, int flag, int[][] besselXY, int width, int height) {
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = (Graphics2D) image.getGraphics();
		// 填充背景颜色
		g2d.setColor(Color.WHITE);
		g2d.fillRect(0, 0, width, height);
		// 抗锯齿
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		// 画干扰圆圈
		g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f * RandomUtil.randomInt(0, 10)));  // 设置透明度
		drawOval(2, width, height, g2d);
		// 画干扰线
		g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));  // 设置透明度
		g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
		g2d.setColor(fontColor[0]);
		CubicCurve2D shape = new CubicCurve2D.Double(besselXY[0][0], besselXY[0][1], besselXY[1][0], besselXY[1][1], besselXY[2][0], besselXY[2][1], besselXY[3][0], besselXY[3][1]);
		g2d.draw(shape);
		// 画验证码
		g2d.setFont(font());
		FontMetrics fontMetrics = g2d.getFontMetrics();
		int fW = (width - 12) / code.length();  // 每一个字符所占的宽度
		int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2;  // 字符的左右边距
		for (int i = 0; i < code.length(); i++) {
			// 设置透明度
			AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i, code.length()));
			g2d.setComposite(ac3);
			g2d.setColor(fontColor[i]);
			int fx = i * fW + fSp + 6;
			int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(code.charAt(i)), g2d).getHeight()) >> 1) - 6;  // 文字的纵坐标
			g2d.drawString(String.valueOf(code.charAt(i)), fx, fY);
		}
		log.trace("font : {}", fontMetrics.getFont());
		g2d.dispose();
		return image;
	}

	/**
	 * 获取透明度,从0到1,自动计算步长
	 *
	 * @param i i
	 * @param j j
	 * @return 透明度
	 */
	private static float getAlpha(int i, int j, int len) {
		int num = i + j;
		float r = (float) 1 / (len - 1);
		float s = len * r;
		return num >= len ? (num * r - s) : num * r;
	}


}
